#include "bignumbers.h"

#include <stdlib.h>

using namespace bignumbers;

BigInt::BigInt(const QString& value, int base)
{
    mpz_init(m_number);

    if (!value.isEmpty())
    {
        mpz_set_str(m_number, value.toAscii().data(), base);
    }
}

BigInt::BigInt(const BigInt& other)
{
    mpz_init_set(m_number, other.m_number);
}

BigInt::~BigInt()
{
    mpz_clear(m_number);
}

QString BigInt::toString(int base) const
{
    QString ret;

    char* buffer = new char[mpz_sizeinbase(m_number, base) + 2];

    mpz_get_str(buffer, base, m_number);

    ret.append(buffer);

    delete buffer;

    return ret;
}

BigInt& BigInt::operator=(const BigInt& other)
{
    if (this != &other) {
        mpz_clear(m_number);

        mpz_init_set(m_number, other.m_number);
    }

    return *this;
}

const BigInt BigInt::operator+(const BigInt& other) const
{
    BigInt ret;

    mpz_add(ret.m_number, this->m_number, other.m_number);

    return ret;
}

const BigInt BigInt::operator-(const BigInt& other) const
{
    BigInt ret;

    mpz_sub(ret.m_number, this->m_number, other.m_number);

    return ret;
}

const BigInt BigInt::operator*(const BigInt& other) const
{
    BigInt ret;

    mpz_mul(ret.m_number, this->m_number, other.m_number);

    return ret;
}

const BigInt BigInt::operator/(const BigInt& other) const
{
    BigInt ret;

    mpz_cdiv_q(ret.m_number, this->m_number, other.m_number);

    return ret;
}

BigFloat::BigFloat(const QString& value)
{
    mpf_init(m_number);

    if (!value.isEmpty())
    {
        mpf_set_str(m_number, value.toAscii().data(), 10);
    }
}

BigFloat::BigFloat(const BigFloat& other)
{
    mpf_init_set(m_number, other.m_number);
}

BigFloat::BigFloat(const BigInt& other)
{
    mpf_init(m_number);

    mpf_set_z(m_number, other.m_number);
}

BigFloat::~BigFloat()
{
    mpf_clear(m_number);
}

QString BigFloat::toString(int decimals) const
{
    QString ret;

    char* buffer;

    mp_exp_t expptr;

    buffer = mpf_get_str(0, &expptr, 10, 100, m_number);

    ret.append(buffer);

    free(buffer);

    if (ret.isEmpty())
    {
        ret = "0";
    }
    else
    {
        if (expptr != -1)
        {
            if (expptr > ret.length())
            {
                const int l = ret.length();

                for (int i = 0; i < (expptr - l); ++i)
                {
                    ret.append("0");
                }
            }

            if (ret.startsWith("-"))
            {
                ++expptr;
            }
            ret.insert(expptr, ".");
        }

        if (ret.endsWith("."))
        {
            ret = ret.left(ret.length() - 1);
        }
        else
        {
            ret = ret.left(expptr + 1 + decimals);
        }

        if (expptr == 0)
        {
            ret.insert(0, "0");
        }
    }

    return ret;
}

BigFloat& BigFloat::operator=(const BigFloat& other)
{
    if (this != &other) {
        mpf_clear(m_number);

        mpf_init_set(m_number, other.m_number);
    }

    return *this;
}

const BigFloat BigFloat::operator+(const BigFloat& other) const
{
    BigFloat ret;

    mpf_add(ret.m_number, this->m_number, other.m_number);

    return ret;
}

const BigFloat BigFloat::operator-(const BigFloat& other) const
{
    BigFloat ret;

    mpf_sub(ret.m_number, this->m_number, other.m_number);

    return ret;
}

const BigFloat BigFloat::operator*(const BigFloat& other) const
{
    BigFloat ret;

    mpf_mul(ret.m_number, this->m_number, other.m_number);

    return ret;
}

const BigFloat BigFloat::operator/(const BigFloat& other) const
{
    BigFloat ret;

    mpf_div(ret.m_number, this->m_number, other.m_number);

    return ret;
}

